package com.fxnotes.book.node;

import com.fxnotes.Resources;
import com.fxnotes.common.FxUtils;
import com.fxnotes.common.ui.components.ModalDialogBuilder;
import com.fxnotes.domain.model.NamedEntity;
import java.util.List;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Parent;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import net.miginfocom.layout.AC;
import net.miginfocom.layout.LC;
import org.tbee.javafx.scene.layout.MigPane;

public class EditEntityComponent<T extends NamedEntity> {

    private final String entityName;
    private MigPane pane;
    private ListView<T> listView;
    private int currentSelectedIndex = FxUtils.INVALID_LIST_INDEX;
    private Button add;
    private Button edit;
    private Button remove;
    private Button moveUp;
    private Button moveDown;
    private Button open;
    private EditEntityHandler<T> editEntityHandler;

    protected EditEntityComponent(EditEntityComponentBuilder<T> builder) {
        this.entityName = builder.getEntityName();
        this.editEntityHandler = builder.getEditEntityHandler();
        
        initUI(builder);
        initHandlers();
    }

    private void initUI(EditEntityComponentBuilder<T> builder) {
        listView = ListViewBuilder.<T>create()
                .prefWidth(builder.getWidth())
                .prefHeight(builder.getHeight())
                .build();
        listView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);

        add = ButtonBuilder.create()
                .text(Resources.ADD)
                .tooltip(FxUtils.createTooltip(Resources.ADD_TOOLTIP))
                .build();

        edit = ButtonBuilder.create()
                .text(Resources.EDIT)
                .tooltip(FxUtils.createTooltip(Resources.EDIT_TOOLTIP))
                .build();

        remove = ButtonBuilder.create()
                .text(Resources.REMOVE)
                .tooltip(FxUtils.createTooltip(Resources.REMOVE_TOOLTIP))
                .build();

        moveUp = ButtonBuilder.create()
                .text(Resources.MOVE_UP)
                .tooltip(FxUtils.createTooltip(Resources.MOVE_UP_TOOLTIP))
                .disable(true)
                .visible(builder.areMoveButtonsVisible())
                .build();

        moveDown = ButtonBuilder.create()
                .text(Resources.MOVE_DOWN)
                .tooltip(FxUtils.createTooltip(Resources.MOVE_DOWN_TOOLTIP))
                .disable(true)
                .visible(builder.areMoveButtonsVisible())
                .build();

        open = ButtonBuilder.create()
                .text(Resources.OPEN)
                .visible(false)
                .build();

        pane = new MigPane(new LC().align("center", "center"), new AC().align("right"));
        pane.add(new Label(builder.getHeader()), "spanx 2, grow, wrap, align left");
        pane.add(listView, "spany 7, grow");
        pane.add(add, "wmin 30, wrap");
        pane.add(edit, "wmin 30, wrap");
        pane.add(remove, "wmin 30, wrap");
        pane.add(new Label(""), "wrap");  //separator
        pane.add(moveUp, "wmin 30, wrap");
        pane.add(moveDown, "wmin 30, wrap");
//        pane.add(open, "cell 0 5, wmin 80");
    }

    private void initHandlers() {
        listView.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {

            @Override
            public void changed(ObservableValue<? extends Number> selection, Number oldIndex, Number newIndex) {
                moveUp.setDisable(isInvalid(newIndex) || isFirst(newIndex));
                moveDown.setDisable(isInvalid(newIndex) || isLast(newIndex));
            }

            private boolean isInvalid(Number index) {
                return index == FxUtils.INVALID_LIST_INDEX;
            }

            private boolean isFirst(Number index) {
                return index == 0;
            }

            private boolean isLast(Number index) {
                return index == (listView.getItems().size() - 1);
            }
        });

        listView.setOnMouseClicked(new EventHandler<MouseEvent>() {

            @Override
            public void handle(MouseEvent event) {
                if(isEmptySelection()) {
                    return;
                }
                
                if (event.getClickCount() == 2) {
                    editEntityHandler.onSelectByDoubleClick(getSelectedItem());
                    
                } else if (event.getClickCount() == 1) {
                    editEntityHandler.onSelectByClick(getSelectedItem());
                }
            }
        });

        add.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {

                final TextField nameField = FxUtils.createNameField("");
                EventHandler<ActionEvent> addHandler = new EventHandler<ActionEvent>() {

                    @Override
                    public void handle(ActionEvent event) {
                        currentSelectedIndex = getSelectedIndex();
                        editEntityHandler.onAdd(nameField.getText());
                    }
                };

                ModalDialogBuilder.create()
                        .owner(pane.getScene().getWindow())
                        .title(getTitle("Add"))
                        .width(250)
                        .content(nameField)
                        .confirmationButton("Save", addHandler)
                        .build()
                        .show();
            }
        });

        edit.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                if (isEmptySelection()) {
                    return;
                }

                final T selectedEntity = getSelectedItem();
                final TextField nameField = FxUtils.createNameField(selectedEntity.getName());

                EventHandler<ActionEvent> editHandler = new EventHandler<ActionEvent>() {

                    @Override
                    public void handle(ActionEvent event) {
                        currentSelectedIndex = getSelectedIndex();
                        editEntityHandler.onUpdate(selectedEntity, nameField.getText());
                    }
                };

                ModalDialogBuilder.create()
                        .owner(pane.getScene().getWindow())
                        .title(getTitle("Edit"))
                        .width(250).content(nameField)
                        .confirmationButton("Save", editHandler)
                        .build()
                        .show();
            }
        });

        remove.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                if (isEmptySelection()) {
                    return;
                }

                final T selectedEntity = getSelectedItem();
                EventHandler<ActionEvent> removeHandler = new EventHandler<ActionEvent>() {

                    @Override
                    public void handle(ActionEvent event) {
                        currentSelectedIndex = FxUtils.INVALID_LIST_INDEX;
                        editEntityHandler.onRemove(selectedEntity);
                    }
                };

                ModalDialogBuilder.create()
                        .owner(pane.getScene().getWindow())
                        .title(getTitle("Remove"))
                        .width(250)
                        .content(new Label("Remove '" + selectedEntity.getName() + "'?"))
                        .confirmationButton("Remove", removeHandler)
                        .build()
                        .show();
            }
        });

        moveUp.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                changeItemPosition(-1);
            }
        });

        moveDown.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                changeItemPosition(1);
            }
        });

        open.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                if (isEmptySelection()) {
                    return;
                }

                editEntityHandler.onSelectByDoubleClick(getSelectedItem());
            }
        });
    }

    // API
    public Parent getNode() {
        return pane;
    }

    public void reload(List<T> entities, boolean focus) {
        listView.getItems().clear();
        listView.getItems().addAll(entities);
        listView.getSelectionModel().select(currentSelectedIndex);
        if (focus) {
            listView.requestFocus();
        }
    }
    
    public void clear(boolean focus) {
        listView.getItems().clear();
        if (focus) {
            listView.requestFocus();
        }
    }    

    // helper methods
    private String getTitle(String title) {
        return title + " " + entityName.toLowerCase();
    }
    
    private boolean isEmptySelection() {
        return listView.getSelectionModel().isEmpty();
    }    
    
    private int getSelectedIndex() {
        return listView.getSelectionModel().getSelectedIndex();
    }

    public T getSelectedItem() {
        return listView.getSelectionModel().getSelectedItem();
    }

    private void changeItemPosition(int offset) {
        // take snapshop
        int selectedIndex = getSelectedIndex();
        T selectedItem = getSelectedItem();
        
        // change position
        listView.getItems().remove(selectedIndex);
        listView.getItems().add(selectedIndex + offset, selectedItem);

        //remember last position
        currentSelectedIndex = selectedIndex + offset;
        
        // notify
        editEntityHandler.onChangePosition(listView.getItems(), getSelectedItem());
    }
}
